/*
* Copyright (C) 2015-2017 PÂRIS Quentin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.phoenicis.tools.archive;
import com.google.common.io.CountingInputStream;
import org.phoenicis.entities.ProgressEntity;
import org.phoenicis.tools.files.FileUtilities;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;
/**
* Tar extraction utilities
*/
public class Tar {
private static final String TAR_ERROR_MESSAGE = "Unable to open input stream";
private final Logger LOGGER = LoggerFactory.getLogger(Tar.class);
private final FileUtilities fileUtilities;
public Tar(FileUtilities fileUtilities) {
this.fileUtilities = fileUtilities;
}
List<File> uncompressTarBz2File(File inputFile, File outputDir, Consumer<ProgressEntity> stateCallback) {
try (CountingInputStream countingInputStream = new CountingInputStream(new FileInputStream(inputFile));
InputStream inputStream = new BZip2CompressorInputStream(countingInputStream)) {
final long finalSize = FileUtils.sizeOf(inputFile);
return uncompress(inputStream, countingInputStream, outputDir, finalSize, stateCallback);
} catch (IOException e) {
throw new ArchiveException(TAR_ERROR_MESSAGE, e);
}
}
List<File> uncompressTarGzFile(File inputFile, File outputDir, Consumer<ProgressEntity> stateCallback) {
try (CountingInputStream countingInputStream = new CountingInputStream(new FileInputStream(inputFile));
InputStream inputStream = new GZIPInputStream(countingInputStream)) {
final long finalSize = FileUtils.sizeOf(inputFile);
return uncompress(inputStream, countingInputStream, outputDir, finalSize, stateCallback);
} catch (IOException e) {
throw new ArchiveException(TAR_ERROR_MESSAGE, e);
}
}
List<File> uncompressTarXzFile(File inputFile, File outputDir, Consumer<ProgressEntity> stateCallback) {
try (CountingInputStream countingInputStream = new CountingInputStream(new FileInputStream(inputFile));
InputStream inputStream = new XZCompressorInputStream(countingInputStream)) {
final long finalSize = FileUtils.sizeOf(inputFile);
return uncompress(inputStream, countingInputStream, outputDir, finalSize, stateCallback);
} catch (IOException e) {
throw new ArchiveException(TAR_ERROR_MESSAGE, e);
}
}
List<File> uncompressTarFile(File inputFile, File outputDir, Consumer<ProgressEntity> stateCallback) {
try (CountingInputStream countingInputStream = new CountingInputStream(new FileInputStream(inputFile))) {
final long finalSize = FileUtils.sizeOf(inputFile);
return uncompress(countingInputStream, countingInputStream, outputDir, finalSize, stateCallback);
} catch (IOException e) {
throw new ArchiveException(TAR_ERROR_MESSAGE, e);
}
}
/**
* Gunzip a file
*
* @param inputFile
* source file
* @param outputFile
* destionation file
* @return the destionation file
* @throws ArchiveException
* if any error occurs
*/
public File gunzip(final File inputFile, final File outputFile) {
LOGGER.info(
String.format("Ungzipping %s to dir %s.", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()));
try (GZIPInputStream in = new GZIPInputStream(new FileInputStream(inputFile));
FileOutputStream out = new FileOutputStream(outputFile)) {
IOUtils.copy(in, out);
return outputFile;
} catch (IOException e) {
throw new ArchiveException("Unable to gunzip file", e);
}
}
/**
* Bunzip2 a file
*
* @param inputFile
* source file
* @param outputFile
* destionation file
* @return the destionation file
* @throws ArchiveException
* if any error occurs
*/
public File bunzip2(final File inputFile, final File outputFile) {
LOGGER.info(
String.format("Ungzipping %s to dir %s.", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()));
try (BZip2CompressorInputStream in = new BZip2CompressorInputStream(new FileInputStream(inputFile));
FileOutputStream out = new FileOutputStream(outputFile)) {
IOUtils.copy(in, out);
return outputFile;
} catch (IOException e) {
throw new ArchiveException("Unable to gunzip file", e);
}
}
/**
* Uncompress a tar
*
* @param countingInputStream
* to count the number of byte extracted
* @param outputDir
* The directory where files should be extracted
* @return A list of extracted files
* @throws ArchiveException
* if the process fails
*/
private List<File> uncompress(final InputStream inputStream, CountingInputStream countingInputStream,
final File outputDir, long finalSize, Consumer<ProgressEntity> stateCallback) {
final List<File> uncompressedFiles = new LinkedList<>();
try (ArchiveInputStream debInputStream = new ArchiveStreamFactory().createArchiveInputStream("tar",
inputStream)) {
TarArchiveEntry entry;
while ((entry = (TarArchiveEntry) debInputStream.getNextEntry()) != null) {
final File outputFile = new File(outputDir, entry.getName());
if (entry.isDirectory()) {
LOGGER.info(
String.format("Attempting to write output directory %s.", outputFile.getAbsolutePath()));
if (!outputFile.exists()) {
LOGGER.info(String.format("Attempting to createPrefix output directory %s.",
outputFile.getAbsolutePath()));
Files.createDirectories(outputFile.toPath());
}
} else {
LOGGER.info(String.format("Creating output file %s (%s).", outputFile.getAbsolutePath(),
entry.getMode()));
if (entry.isSymbolicLink()) {
Files.createSymbolicLink(Paths.get(outputFile.getAbsolutePath()),
Paths.get(entry.getLinkName()));
} else {
try (final OutputStream outputFileStream = new FileOutputStream(outputFile)) {
IOUtils.copy(debInputStream, outputFileStream);
Files.setPosixFilePermissions(Paths.get(outputFile.getPath()),
fileUtilities.octToPosixFilePermission(entry.getMode()));
}
}
}
uncompressedFiles.add(outputFile);
stateCallback
.accept(new ProgressEntity.Builder()
.withPercent(
(double) countingInputStream.getCount() / (double) finalSize * (double) 100)
.withProgressText("Extracting " + outputFile.getName()).build());
}
return uncompressedFiles;
} catch (IOException | org.apache.commons.compress.archivers.ArchiveException e) {
throw new ArchiveException("Unable to extract the file", e);
}
}
}